home *** CD-ROM | disk | FTP | other *** search
/ Compendium Deluxe 1 / LSD Compendium Deluxe 1.iso / a / intuition / menu / popupm10.lha / PopUpMenuClass_1.0 / lib_source / PopUpMenuClass.c < prev   
Encoding:
C/C++ Source or Header  |  1993-12-23  |  28.3 KB  |  934 lines

  1. /** ** *** MakeRev Header *** **
  2. **
  3. **  ** PopUpMenu Gadget Class - BOOPSI gadget class. **
  4. **
  5. **  Copyright © 1993 Markus Aalto
  6. **
  7. **  Creation date: 09-Dec-93
  8. **
  9. **  ------------------------------------------------------------------
  10. **  $Filename: PopUpMenuClass.c $
  11. **  $Revision: 1.0 $
  12. **  $Date: 23-Dec-93 $
  13. **
  14. **  $Author: Markus Aalto $
  15. **  $Comment: Distributed under the GNU General Public Licence. $
  16. **
  17. */
  18.  
  19. /* Make sure everything is added from PopUpMenuClass.h */
  20. #define POPUPMENUCLASS_PRIVATE  1
  21.  
  22. #include    "PopUpMenuClass.h"
  23. #include    <string.h>
  24. #include    <math.h>
  25.  
  26. /* Version. */
  27. static UBYTE popupmenuclass_version[] = "$VER: PopUpMenuClass 1.0 (23.12.93)";
  28.  
  29. /* STATIC Protos for real Method functions and for class dispatcher. */
  30. static ULONG    __saveds __asm PUMG_Dispatcher( register __a0 Class *cl,
  31.                                                 register __a2 Object *o,
  32.                                                 register __a1 Msg msg);
  33.  
  34. static ULONG    PUMG_NEW( Class *cl,
  35.                           Object *o,
  36.                           struct opSet *ops );
  37.  
  38. static ULONG    PUMG_DISPOSE( Class *cl,
  39.                               Object *o,
  40.                               Msg msg );
  41.  
  42. static ULONG    PUMG_SET( Class *cl,
  43.                           Object *o,
  44.                           struct opSet *ops );
  45.  
  46. static ULONG    PUMG_GET( Class *cl,
  47.                           Object *o,
  48.                           struct opGet *opg );
  49.  
  50. static ULONG    PUMG_UPDATE( Class *cl,
  51.                              Object *o,
  52.                              struct opUpdate *opu );
  53.  
  54. static ULONG    PUMG_NOTIFY( Class *cl,
  55.                              Object *o,
  56.                              struct opUpdate *opu );
  57.  
  58. static ULONG    PUMG_RENDER( Class *cl,
  59.                              Object *o,
  60.                              struct gpRender *gpr );
  61.  
  62. static ULONG    PUMG_GOACTIVE( Class *cl,
  63.                                Object *o,
  64.                                struct gpInput *gpi );
  65.  
  66. static ULONG    PUMG_HANDLEINPUT( Class *cl,
  67.                                   Object *o,
  68.                                   struct gpInput *gpi );
  69.  
  70. static ULONG    PUMG_GOINACTIVE( Class *cl,
  71.                                  Object *o,
  72.                                  struct gpGoInactive *gpgi );
  73.  
  74. /* Protos for static help functions. */
  75.  
  76. static void     PUMG_MakeCheckings( PUMGData *PD );
  77.  
  78. static void     PUMG_GetGadgetRect( Object *o,
  79.                                     struct GadgetInfo *gi,
  80.                                     struct Rectangle *rect );
  81.  
  82. static void     PUMG_DrawPopupWindow( PUMGData *PD,
  83.                                       struct DrawInfo *dri,
  84.                                       ULONG From,
  85.                                       LONG Count );
  86.  
  87. static void     PUMG_DrawFrame( struct Window *win,
  88.                                 int order,
  89.                                 struct DrawInfo *dri,
  90.                                 UBYTE *name,
  91.                                 struct TextFont *tf,
  92.                                 BOOL Active );
  93.  
  94. static void     PUMG_DrawPopupMenuImage( struct RastPort *RP,
  95.                                          ULONG Pen1,
  96.                                          ULONG Pen2,
  97.                                          ULONG Left,
  98.                                          ULONG Top,
  99.                                          LONG Height );
  100.  
  101. /*******************************************************************/
  102. /*******************************************************************/
  103. /* The real code starts here.                                      */
  104. /*******************************************************************/
  105. /*******************************************************************/
  106.  
  107. /* Global Create PopUpMenuClass function. */
  108.  
  109. Class *CreatePopUpMenuClass()
  110. {
  111.     Class *cl;
  112.  
  113.     cl = MakeClass( NULL, GADGETCLASS, NULL, sizeof(PUMGData), 0);
  114.     if( cl ) {
  115.         cl->cl_Dispatcher.h_Entry = (HookFunction)PUMG_Dispatcher;
  116.     }
  117.  
  118.     return( cl );
  119. }
  120.  
  121. /* Global Free PopUpMenuClass function. */
  122. BOOL DisposePopUpMenuClass( Class * cl )
  123. {
  124.     return( FreeClass( cl ) ) ;
  125. }
  126.  
  127.  
  128. /*******************************************************************/
  129. /*******************************************************************/
  130. /* PopUpMenu specific class code.                                  */
  131. /*******************************************************************/
  132. /*******************************************************************/
  133.  
  134. static ULONG __saveds __asm PUMG_Dispatcher( register __a0 Class *cl,
  135.                                              register __a2 Object *o,
  136.                                              register __a1 Msg msg)
  137. {
  138.     ULONG retval;
  139.  
  140.     switch( msg->MethodID )
  141.     {
  142.     case OM_NEW:
  143.         retval = PUMG_NEW(cl, o, (struct opSet *)msg );
  144.         break;
  145.     case OM_DISPOSE:
  146.         retval = PUMG_DISPOSE( cl, o, msg );
  147.         break;
  148.     case OM_SET:
  149.         retval = PUMG_SET(cl, o, (struct opSet *)msg );
  150.         break;
  151.     case OM_GET:
  152.         retval = PUMG_GET( cl, o, (struct opGet *)msg );
  153.         break;
  154.     case OM_UPDATE:
  155.         retval = PUMG_UPDATE( cl, o, (struct opUpdate *)msg );
  156.         break;
  157.     case OM_NOTIFY:
  158.         retval = PUMG_NOTIFY( cl, o, (struct opUpdate *)msg );
  159.         break;
  160.     case GM_RENDER:
  161.         retval = PUMG_RENDER( cl, o, (struct gpRender *)msg );
  162.         break;
  163.     case GM_GOACTIVE:
  164.         retval = PUMG_GOACTIVE( cl, o, (struct gpInput *)msg );
  165.         break;
  166.     case GM_HANDLEINPUT:
  167.         retval = PUMG_HANDLEINPUT( cl, o, (struct gpInput *)msg );
  168.         break;
  169.     case GM_GOINACTIVE:
  170.         retval = PUMG_GOINACTIVE( cl, o, (struct gpGoInactive *)msg );
  171.         break;
  172.     default:
  173.         retval = DoSuperMethodA(cl, o, msg);
  174.         break;
  175.     }
  176.  
  177.  
  178.     return(retval);
  179. }
  180.  
  181. static ULONG PUMG_NEW( Class *cl,
  182.                        Object *o,
  183.                        struct opSet *ops )
  184. {
  185.     Object *object;
  186.     PUMGData *PD;
  187.  
  188.     object = (Object *)DoSuperMethodA( cl, o, (Msg)ops );
  189.     if( object ) {
  190.         PD = INST_DATA( cl, object );
  191.  
  192.         PD->Labels = (struct List *)GetTagData( PUMG_Labels, NULL, ops->ops_AttrList );
  193.         PD->Active = GetTagData( PUMG_Active, 0, ops->ops_AttrList );
  194.  
  195.         PUMG_MakeCheckings( PD );
  196.  
  197.         PD->Font = (struct TextFont *)GetTagData( PUMG_TextFont, NULL,
  198.             ops->ops_AttrList);
  199.  
  200.         PD->FrameImage = (struct Image *)NewObject(NULL,"frameiclass",
  201.                 IA_Recessed,    FALSE,
  202.                 IA_EdgesOnly,   FALSE,
  203.                 IA_FrameType,   FRAME_BUTTON,
  204.                 TAG_END);
  205.         if( PD->FrameImage == NULL ) {
  206.             CoerceMethod(cl, o, OM_DISPOSE);
  207.             object = NULL;
  208.         }
  209.     }
  210.  
  211.     return( (ULONG)object );
  212. }
  213.  
  214.  
  215. static ULONG PUMG_DISPOSE( Class *cl,
  216.                            Object *o,
  217.                            Msg msg )
  218. {
  219.     PUMGData *PD = INST_DATA( cl, o );
  220.  
  221.     if( PD->popup_window ) {
  222.         CloseWindow( PD->popup_window );
  223.     }
  224.  
  225.     if( PD->FrameImage ) DisposeObject( PD->FrameImage );
  226.  
  227.     return( DoSuperMethodA(cl, o, msg) );
  228. }
  229.  
  230.  
  231. static ULONG PUMG_SET( Class *cl,
  232.                        Object *o,
  233.                        struct opSet *ops )
  234. {
  235.     ULONG retval;
  236.     PUMGData *PD = INST_DATA( cl, o );
  237.     struct TagItem *tag, notify;
  238.     UWORD old_active;
  239.  
  240.     retval = DoSuperMethodA( cl, o, (Msg)ops );
  241.  
  242.     /* I decided that it would be best that the values which are
  243.     ** specific to this class, could bot be changed when we have
  244.     ** our PopUpMenu window opened. */
  245.     if( (ops->ops_AttrList != NULL) && (PD->popup_window == NULL) ) {
  246.         tag = FindTagItem( PUMG_Labels, ops->ops_AttrList );
  247.         if( tag ) {
  248.             PD->Labels = (struct List *)tag->ti_Data;
  249.             retval = TRUE;
  250.         }
  251.  
  252.         old_active = PD->Active;
  253.  
  254.         tag = FindTagItem( PUMG_Active, ops->ops_AttrList );
  255.         if( tag ) {
  256.             PD->Active = tag->ti_Data;
  257.             retval = TRUE;
  258.         }
  259.  
  260.         PUMG_MakeCheckings( PD );
  261.  
  262.         tag = FindTagItem( PUMG_TextFont, ops->ops_AttrList );
  263.         if( tag ) {
  264.             PD->Font = (struct TextFont *)tag->ti_Data;
  265.             retval = TRUE;
  266.         }
  267.  
  268.         if( old_active != PD->Active ) {
  269.             /* We send ourselves a OM_NOTIFY message, which will
  270.             ** eventually be broadcasted as OM_UPDATE message
  271.             ** to the target object. Note that we don't send it
  272.             ** simply to our parent, but to ourselves, so if
  273.             ** we have a children which needs to add it's own
  274.             ** data it will be added. */
  275.             PUMG_SetTagArg( notify, TAG_END, NULL );
  276.             (VOID)DoMethod( o, OM_NOTIFY, ¬ify, ops->ops_GInfo, 0 );
  277.         }
  278.     }
  279.  
  280.     return( retval );
  281. }
  282.  
  283. static ULONG PUMG_GET( Class *cl,
  284.                        Object *o,
  285.                        struct opGet *opg )
  286. {
  287.     ULONG retval;
  288.     PUMGData *PD = INST_DATA( cl, o );
  289.  
  290.     switch( opg->opg_AttrID )
  291.     {
  292.     case PUMG_Labels:
  293.         *opg->opg_Storage = (ULONG)PD->Labels;
  294.         retval = TRUE;
  295.         break;
  296.     case PUMG_Active:
  297.         *opg->opg_Storage = (ULONG)PD->Active;
  298.         retval = TRUE;
  299.         break;
  300.     case PUMG_TextFont:
  301.         *opg->opg_Storage = (ULONG)PD->Font;
  302.         retval = TRUE;
  303.         break;
  304.     default:
  305.         retval = DoSuperMethodA(cl, o, (Msg)opg);
  306.         break;
  307.     }
  308.  
  309.     return( retval );
  310. }
  311.  
  312.  
  313. static ULONG PUMG_UPDATE( Class *cl,
  314.                           Object *o,
  315.                           struct opUpdate *opu )
  316. {
  317.     ULONG retval;
  318.     PUMGData *PD = INST_DATA( cl, o );
  319.     struct TagItem *tag, notify;
  320.     struct RastPort *rp;
  321.  
  322.     retval = DoSuperMethodA(cl, o, opu);
  323.  
  324.     /* Update only if gadget isn't currently manipulated. */
  325.     if( PD->popup_window == NULL ) {
  326.         if( opu->opu_AttrList ) {
  327.             tag = FindTagItem( PUMG_Active, opu->opu_AttrList );
  328.             if( tag ) {
  329.                 if( tag->ti_Data != PD->Active ) {
  330.                     PD->Active = tag->ti_Data;
  331.  
  332.                     PUMG_MakeCheckings( PD );
  333.  
  334.                     rp = ObtainGIRPort( opu->opu_GInfo );
  335.                     if( rp ) {
  336.                         DoMethod( o, GM_RENDER, opu->opu_GInfo, rp, GREDRAW_UPDATE );
  337.                         ReleaseGIRPort( rp );
  338.                     }
  339.  
  340.                     /* Notify the change. */
  341.                     PUMG_SetTagArg( notify, TAG_END, NULL );
  342.                     (void)DoMethod( o, OM_NOTIFY, ¬ify, opu->opu_GInfo, 0 );
  343.                 }
  344.             }
  345.         }
  346.     }
  347.  
  348.     return( retval );
  349. }
  350.  
  351.  
  352. static ULONG PUMG_NOTIFY( Class *cl,
  353.                           Object *o,
  354.                           struct opUpdate *opu )
  355. {
  356.     struct TagItem tags[3];
  357.     PUMGData *PD = INST_DATA( cl, o );
  358.  
  359.     PUMG_SetTagArg(tags[0], GA_ID, ((struct Gadget *)o)->GadgetID);
  360.     PUMG_SetTagArg(tags[1], PUMG_Active, PD->Active );
  361.  
  362.     /* If there are no previous tags in OM_NOTIFY message, we
  363.     ** add them there as only ones. Otherwise we tag previous
  364.     ** tags to the end of our tags. Got it? :')
  365.     */
  366.     if( opu->opu_AttrList == NULL ) {
  367.         PUMG_SetTagArg(tags[2], TAG_END, NULL);
  368.     }
  369.     else PUMG_SetTagArg(tags[2], TAG_MORE, opu->opu_AttrList );
  370.  
  371.     return( DoSuperMethod(cl, o, OM_NOTIFY, tags, opu->opu_GInfo, opu->opu_Flags) );
  372. }
  373.  
  374. static ULONG PUMG_RENDER( Class *cl,
  375.                           Object *o,
  376.                           struct gpRender *gpr )
  377. {
  378.     ULONG retval, State;
  379.     struct Gadget *gad = (struct Gadget *)o;
  380.     struct Rectangle rect;
  381.     struct DrawInfo *dri;
  382.     struct IBox container;
  383.     struct Node *node;
  384.     struct TextExtent temp_te;
  385.     struct RastPort *RP = gpr->gpr_RPort;
  386.     UWORD BorderWidth, BorderHeight, TextWidth;
  387.     UWORD patterndata[2] = { 0x2222, 0x8888 };
  388.     ULONG TextPen, ImagePen1, ImagePen2;
  389.     PUMGData *PD = INST_DATA( cl, o );
  390.  
  391.     retval = DoSuperMethodA(cl, o, gpr);
  392.  
  393.     /* Get real Min and Max positions. */
  394.     PUMG_GetGadgetRect( o, gpr->gpr_GInfo, &rect );
  395.  
  396.     /* Calculate real dimensions. */
  397.     container.Left = rect.MinX; container.Top = rect.MinY;
  398.     container.Width = 1 + rect.MaxX - rect.MinX;
  399.     container.Height = 1 + rect.MaxY - rect.MinY;
  400.  
  401.     dri = gpr->gpr_GInfo->gi_DrInfo;
  402.  
  403.     if( gad->Flags & GFLG_DISABLED ) {
  404.         State = IDS_DISABLED;
  405.     }
  406.     else if( gad->Flags & GFLG_SELECTED ) {
  407.         State = IDS_SELECTED;
  408.     }
  409.     else State = IDS_NORMAL;
  410.  
  411.     /* Frame rendering goes here. */
  412.     SetAttrs( PD->FrameImage,
  413.             IA_Left,    container.Left,
  414.             IA_Top,     container.Top,
  415.             IA_Width,   container.Width,
  416.             IA_Height,  container.Height,
  417.             TAG_END);
  418.  
  419.     DrawImageState( RP, PD->FrameImage, 0, 0, State, dri);
  420.  
  421.     if( dri ) {
  422.         TextPen = dri->dri_Pens[TEXTPEN];
  423.         ImagePen1 = dri->dri_Pens[SHINEPEN];
  424.         ImagePen2 = dri->dri_Pens[SHADOWPEN];
  425.     }
  426.     else { /* If for some unknown reason Drawinfo is NULL then we
  427.            ** Use these predefined values, which should work atleast
  428.            ** for current OS releases. */
  429.         TextPen = ImagePen2 = 1;
  430.         ImagePen1 = 2;
  431.     }
  432.  
  433.     /* Draw the PopupMenu Image. */
  434.     PUMG_DrawPopupMenuImage( RP, ImagePen1, ImagePen2,
  435.         5LU + container.Left, 2LU + container.Top, -5L + container.Height);
  436.  
  437.     /*******************************/
  438.     /* Text rendering starts here. */
  439.     /*******************************/
  440.  
  441.     /* Do we have a proper font. */
  442.     if( PD->Font == NULL ) {
  443.         /* If not we use the font we have in RastPort. */
  444.         PD->Font = RP->Font;
  445.     }
  446.     else SetFont( RP, PD->Font );
  447.  
  448.     /* Check if we have nothing to print. */
  449.     if( PD->Count > 0 ) {
  450.         ULONG len, i = 0;
  451.         char *label_name;
  452.  
  453.         node = PD->Labels->lh_Head;
  454.         while( node->ln_Succ ) {
  455.             if( i == PD->Active ) {
  456.                 label_name = node->ln_Name;
  457.                 if( label_name ) {
  458.                     len = TextFit( RP, label_name, (ULONG)strlen(label_name),
  459.                         &temp_te, NULL, 1, (ULONG)container.Width - 28,
  460.                         (ULONG)( 1 + PD->Font->tf_YSize ) );
  461.  
  462.                     TextWidth = 1 + temp_te.te_Extent.MaxX - temp_te.te_Extent.MinX;
  463.  
  464.                     SetAPen(RP, TextPen);
  465.                     Move( RP, (LONG)( 10 + container.Left + (container.Width - TextWidth)/2
  466.                         - temp_te.te_Extent.MinX ), (LONG)
  467.                         PD->Font->tf_Baseline + (1 + container.Top + rect.MaxY
  468.                         - PD->Font->tf_YSize)/2 );
  469.  
  470.                     Text( RP, label_name, len );
  471.                 }
  472.  
  473.                 /* End the drawing. */
  474.                 break;
  475.             }
  476.             else {
  477.                 i++;
  478.                 node = node->ln_Succ;
  479.             }
  480.         }
  481.     }
  482.  
  483.     /* Disabled pattern rendering is here. */
  484.     if( State == IDS_DISABLED ) {
  485.         BorderHeight = 1;
  486.         BorderWidth = (IntuitionBase->LibNode.lib_Version < 39) ? 1 : 2;
  487.  
  488.         container.Left += BorderWidth;
  489.         container.Top += BorderHeight;
  490.         container.Width = max( 1, container.Width - 2*BorderWidth );
  491.         container.Height = max( 1, container.Height - 2*BorderHeight );
  492.  
  493.         SetDrMd(RP,JAM1);
  494.         SetAfPt(RP, patterndata, 1);
  495.  
  496.         RectFill(RP,  (LONG)container.Left, (LONG)container.Top,
  497.                 -1L + container.Left + container.Width,
  498.                 -1L + container.Top + container.Height );
  499.  
  500.         SetAfPt(RP, NULL, 0 );
  501.     }
  502.  
  503.     /* Copy current Rectangle. */
  504.     PD->rect = rect;
  505.  
  506.     return( retval );
  507. }
  508.  
  509.  
  510. static ULONG PUMG_GOACTIVE( Class *cl,
  511.                             Object *o,
  512.                             struct gpInput *gpi )
  513. {
  514.     ULONG retval = GMR_MEACTIVE, Left, Top;
  515.     struct RastPort *rp;
  516.     PUMGData *PD = INST_DATA( cl, o );
  517.     struct GadgetInfo *gi = gpi->gpi_GInfo;
  518.     struct Gadget *gad = (struct Gadget *)o;
  519.  
  520.     /* Test if we are disabled. */
  521.     if( gad->Flags & GFLG_DISABLED ) return( GMR_NOREUSE );
  522.  
  523.     /* Call first our parent class. */
  524.     (void)DoSuperMethodA(cl, o, gpi);
  525.  
  526.     /* Chech whether we were activated from mouse or keyboard. */
  527.     PD->ActiveFromMouse = (gpi->gpi_IEvent != NULL);
  528.  
  529.     /* Select this gadget. */
  530.     gad->Flags |= GFLG_SELECTED;
  531.  
  532.     rp = ObtainGIRPort( gi );
  533.     if( rp ) {
  534.         /* Render ourselves as selected gadget. */
  535.         DoMethod( o, GM_RENDER, gi, rp, GREDRAW_UPDATE );
  536.         ReleaseGIRPort( rp );
  537.  
  538.         /* Get the domain top/left position. */
  539.         Left = gi->gi_Domain.Left;
  540.         Top = gi->gi_Domain.Top;
  541.  
  542.         /* If this is window, we have to add window Left/Top values too. */
  543.         if( gi->gi_Window ) {
  544.             Left += gi->gi_Window->LeftEdge;
  545.             Top += gi->gi_Window->TopEdge;
  546.         }
  547.  
  548.         /* Count how many items fits to menu. */
  549.         PD->FitsItems = (gi->gi_Screen->Height - 4) / (5 + PD->Font->tf_YSize);
  550.         if( PD->FitsItems > PD->Count ) {
  551.             PD->FitsItems = PD->Count;
  552.         }
  553.  
  554.         PD->popup_window = OpenWindowTags(NULL,
  555.             WA_Left,            Left + PD->rect.MinX,
  556.             WA_Top,             1 + Top + PD->rect.MaxY,
  557.             WA_Width,           1 + PD->rect.MaxX - PD->rect.MinX,
  558.             WA_Height,          4 + PD->FitsItems*(5 + PD->Font->tf_YSize),
  559.             WA_Activate,        FALSE,
  560.             WA_CustomScreen,    gi->gi_Screen,
  561.             WA_SizeGadget,      FALSE,
  562.             WA_DragBar,         FALSE,
  563.             WA_DepthGadget,     FALSE,
  564.             WA_CloseGadget,     FALSE,
  565.             WA_Borderless,      TRUE,
  566.             WA_Flags,           0,
  567.             WA_AutoAdjust,      TRUE,
  568.             WA_RMBTrap,         TRUE,
  569.             WA_SimpleRefresh,   TRUE,
  570.             WA_NoCareRefresh,   TRUE,
  571.             TAG_END );
  572.  
  573.         if( PD->popup_window == NULL ) {
  574.             retval = GMR_NOREUSE;
  575.         }
  576.         else {
  577.             /* We make sure Active item isn't too large to display. */
  578.             if( PD->FitsItems < PD->Active ) PD->Active = PD->FitsItems-1;
  579.  
  580.             /* If activated from keyboard we can set temporary value
  581.             ** to currently activated item. Otherwise we set it
  582.             ** to -1 which means that there is no active item. */
  583.             PD->Temp_Active = PD->ActiveFromMouse ? (ULONG)~0 : PD->Active;
  584.  
  585.             /* Render all items. */
  586.             PUMG_DrawPopupWindow( PD, gi->gi_DrInfo, 0, -1);
  587.         }
  588.     }
  589.     else retval = GMR_NOREUSE;
  590.  
  591.     return(retval);
  592. }
  593.  
  594. static ULONG PUMG_HANDLEINPUT( Class *cl,
  595.                                Object *o,
  596.                                struct gpInput *gpi )
  597. {
  598.     ULONG retval = GMR_MEACTIVE;
  599.     struct InputEvent *ie = gpi->gpi_IEvent;
  600.     PUMGData *PD = INST_DATA(cl, o);
  601.     WORD X, Y;
  602.     WORD count, old_active;
  603.     struct GadgetInfo *gi = gpi->gpi_GInfo;
  604.     struct TagItem tags;    /* If our possible child class, doesn't know
  605.                             ** how to handle NULL AttrList ptr, then
  606.                             ** this can save lots of crashes. */
  607.  
  608.     /* If there is anykind of AutoPoint program then our main window
  609.     ** might get inactive and we wouldn't get any more messages.
  610.     ** So we check out that we are active and deactivate ourselves
  611.     ** if our window isn't active anymore. */
  612.     if( gi->gi_Window ) {
  613.         if( (gi->gi_Window->Flags & WFLG_WINDOWACTIVE) == 0 ) {
  614.             return( GMR_NOREUSE );
  615.         }
  616.     }
  617.  
  618.     if( PD->ActiveFromMouse ) {
  619.         X = PD->popup_window->MouseX;
  620.         Y = PD->popup_window->MouseY;
  621.  
  622.         count = ( (Y - 2) >= 0 ) ? (Y - 2) / (5 + PD->Font->tf_YSize) : ~0;
  623.  
  624.         old_active = PD->Temp_Active;
  625.  
  626.         if( (X > 2) && (X < (PD->popup_window->Width - 2))
  627.                 && (count >= 0) && (count < PD->FitsItems) ) {
  628.             PD->Temp_Active = (UWORD)count;
  629.         }
  630.         else PD->Temp_Active = (UWORD)~0;
  631.  
  632.         if( old_active != (WORD)PD->Temp_Active ) {
  633.             PUMG_DrawPopupWindow( PD, gi->gi_DrInfo,(ULONG)PD->Temp_Active, 1 );
  634.             PUMG_DrawPopupWindow( PD, gi->gi_DrInfo,(ULONG)old_active, 1 );
  635.         }
  636.  
  637.         while( ie && (retval == GMR_MEACTIVE) ) {
  638.             if( ie->ie_Class == IECLASS_RAWMOUSE ) {
  639.                 if( ie->ie_Code == SELECTUP ) {
  640.                     retval = GMR_NOREUSE;
  641.  
  642.                     if( (PD->Temp_Active != (UWORD)~0) ) {
  643.                         PD->Active = PD->Temp_Active;
  644.                         PUMG_MakeCheckings( PD );
  645.  
  646.                         PUMG_SetTagArg(tags, TAG_END, NULL);
  647.                         (VOID)DoMethod( o, OM_NOTIFY, &tags, gi, 0);
  648.  
  649.                         retval |= GMR_VERIFY;
  650.                         *gpi->gpi_Termination = (ULONG)PD->Active;
  651.                     }
  652.                 }
  653.             }
  654.  
  655.             ie = ie->ie_NextEvent;
  656.         }
  657.     }
  658.     else {
  659.         while( ie && (retval == GMR_MEACTIVE) ) {
  660.             switch( ie->ie_Class )
  661.             {
  662.             case IECLASS_RAWMOUSE:
  663.                 if( ie->ie_Code != IECODE_NOBUTTON ) {
  664.                     retval = GMR_REUSE; /* Reuse the InputEvent. */
  665.                 }
  666.                 break;
  667.             case IECLASS_RAWKEY:
  668.                 old_active = PD->Temp_Active;
  669.                 switch( ie->ie_Code )
  670.                 {
  671.                 case CURSORDOWN:
  672.                     if( ie->ie_Qualifier & (ALTLEFT|ALTRIGHT) ) {
  673.                         PD->Temp_Active = PD->FitsItems-1;  /* Jump to end. */
  674.                     }
  675.                     else if( PD->Temp_Active < (PD->FitsItems-1) ) {
  676.                         PD->Temp_Active += 1;
  677.                     }
  678.                     break;
  679.                 case CURSORUP:
  680.                     if( ie->ie_Qualifier & (ALTLEFT|ALTRIGHT) ) {
  681.                         PD->Temp_Active = 0;    /* Jump to start. */
  682.                     }
  683.                     else if( PD->Temp_Active > 0 ) {
  684.                         PD->Temp_Active -= 1;
  685.                     }
  686.                     break;
  687.                 case 0x45:  /* ESC key. */
  688.                     retval = GMR_NOREUSE;
  689.                     break;
  690.                 case 0x44:  /* RETURN key. */
  691.                     PD->Active = PD->Temp_Active;
  692.                     PUMG_MakeCheckings( PD );
  693.  
  694.                     PUMG_SetTagArg(tags, TAG_END, NULL);
  695.                     (VOID)DoMethod( o, OM_NOTIFY, &tags, gi, 0);
  696.  
  697.                     retval = GMR_NOREUSE|GMR_VERIFY;
  698.                     *gpi->gpi_Termination = (ULONG)PD->Active;
  699.                     break;
  700.                 }
  701.  
  702.                 /* Update the popupwindow items, if changes were made. */
  703.                 if( old_active != PD->Temp_Active ) {
  704.                     PUMG_DrawPopupWindow( PD, gi->gi_DrInfo,
  705.                         (ULONG)PD->Temp_Active, 1 );
  706.                     PUMG_DrawPopupWindow( PD, gi->gi_DrInfo,
  707.                         (ULONG)old_active, 1 );
  708.                 }
  709.             }
  710.  
  711.             ie = ie->ie_NextEvent;
  712.         }
  713.     }
  714.  
  715.     return(retval);
  716. }
  717.  
  718.  
  719. static ULONG PUMG_GOINACTIVE( Class *cl,
  720.                               Object *o,
  721.                               struct gpGoInactive *gpgi )
  722. {
  723.     ULONG retval;
  724.     struct RastPort *rp;
  725.     PUMGData *PD = INST_DATA(cl, o);
  726.  
  727.     retval = DoSuperMethodA(cl, o, gpgi);
  728.  
  729.     ((struct Gadget *)o)->Flags &= ~GFLG_SELECTED;
  730.  
  731.     rp = ObtainGIRPort( gpgi->gpgi_GInfo );
  732.     if( rp ) {
  733.         DoMethod( o, GM_RENDER, gpgi->gpgi_GInfo, rp, GREDRAW_UPDATE );
  734.         ReleaseGIRPort( rp );
  735.     }
  736.  
  737.     if( PD->popup_window ) {
  738.         CloseWindow(PD->popup_window);
  739.         PD->popup_window = NULL;
  740.     }
  741.  
  742.     return(retval);
  743. }
  744.  
  745. /* Static functions for help with real Method functions. */
  746.  
  747. static void PUMG_MakeCheckings( PUMGData *PD )
  748. {
  749.     struct Node *node;
  750.  
  751.     PD->Count = 0;
  752.  
  753.     if( PD->Labels == NULL ) {
  754.         PD->Active = 0;
  755.     }
  756.     else if( PD->Labels != (struct List *)~0 ) {
  757.         node = (struct Node *)PD->Labels->lh_Head;
  758.         while( node->ln_Succ ) {
  759.             PD->Count += 1;
  760.             node = node->ln_Succ;
  761.         }
  762.  
  763.         if( PD->Active >= PD->Count ) {
  764.             PD->Active = PD->Count + (PD->Count == 0) - 1;
  765.         }
  766.     }
  767. }
  768.  
  769. static void PUMG_GetGadgetRect( Object *o,
  770.                                 struct GadgetInfo *gi,
  771.                                 struct Rectangle *rect )
  772. {
  773.     struct Gadget *gad = (struct Gadget *)o;
  774.     LONG W, H;
  775.  
  776.     rect->MinX = gad->LeftEdge;
  777.     rect->MinY = gad->TopEdge;
  778.     W = gad->Width;
  779.     H = gad->Height;
  780.  
  781.     if( gi ) {
  782.         if( gad->Flags & GFLG_RELRIGHT ) rect->MinX += gi->gi_Domain.Width - 1;
  783.         if( gad->Flags & GFLG_RELBOTTOM ) rect->MinY += gi->gi_Domain.Height - 1;
  784.         if( gad->Flags & GFLG_RELWIDTH ) W += gi->gi_Domain.Width;
  785.         if( gad->Flags & GFLG_RELHEIGHT ) H += gi->gi_Domain.Height;
  786.     }
  787.  
  788.     rect->MaxX = rect->MinX + W - (W > 0);
  789.     rect->MaxY = rect->MinY + H - (H > 0);
  790. }
  791.  
  792. static void PUMG_DrawPopupWindow( PUMGData *PD,
  793.                                   struct DrawInfo *dri,
  794.                                   ULONG From, LONG Count)
  795. {
  796.     int i, End;
  797.     struct Node *node;
  798.     struct Window *win = PD->popup_window;
  799.     struct RastPort *RP = win->RPort;
  800.  
  801.     if( PD->Count && dri ) {
  802.         /* If we want to draw all entries then we draw
  803.         ** window borders too. */
  804.         if( Count == -1) {
  805.             Count = PD->FitsItems;
  806.             SetAPen( RP, (ULONG)dri->dri_Pens[SHINEPEN]);
  807.             Move( RP, 0, (LONG)win->Height - 1 );
  808.             Draw( RP, 0, 0 );
  809.             Draw( RP, (LONG)win->Width - 1, 0 );
  810.             SetAPen( RP, (ULONG)dri->dri_Pens[SHADOWPEN]);
  811.             Draw( RP, (LONG)win->Width - 1, (LONG)win->Height - 1 );
  812.             Draw( RP, 1, (LONG)win->Height - 1 );
  813.         }
  814.  
  815.         SetFont( RP, PD->Font );
  816.         SetDrMd( RP, JAM1);
  817.  
  818.         node = PD->Labels->lh_Head;
  819.  
  820.         for( i = 0, End = From + Count; node->ln_Succ ; i++ ) {
  821.             if( i < PD->FitsItems ) {
  822.                 if( (i >= From) && ( i < End ) ) {
  823.                     PUMG_DrawFrame( PD->popup_window, i, dri, node->ln_Name,
  824.                         PD->Font, (BOOL)(i == PD->Temp_Active) );
  825.                 }
  826.                 else if( i >= End ) return;
  827.             }
  828.  
  829.             node = node->ln_Succ;
  830.         }
  831.     }
  832. }
  833.  
  834. static void PUMG_DrawFrame( struct Window *win,
  835.                             int order,
  836.                             struct DrawInfo *dri,
  837.                             UBYTE *name,
  838.                             struct TextFont *tf,
  839.                             BOOL Active )
  840. {
  841.     ULONG   Pen1, Pen2, TextPen, BPen;
  842.     ULONG   Top, Width, Bottom, MaxX,
  843.             Len, TextWidth,
  844.             font_height = tf->tf_YSize;
  845.     struct  RastPort *RP = win->RPort;
  846.     struct  TextExtent temp_te;
  847.  
  848.     if( Active ) {
  849.         Pen1 = dri->dri_Pens[SHADOWPEN];
  850.         Pen2 = dri->dri_Pens[SHINEPEN];
  851.         BPen = dri->dri_Pens[FILLPEN];
  852.     }
  853.     else {
  854.         Pen2 = dri->dri_Pens[SHADOWPEN];
  855.         Pen1 = dri->dri_Pens[SHINEPEN];
  856.         BPen = dri->dri_Pens[BACKGROUNDPEN];
  857.     }
  858.  
  859.     TextPen = dri->dri_Pens[TEXTPEN];
  860.  
  861.     Top = 2 + order * (5 + font_height);
  862.     Bottom = 4 + Top + font_height;
  863.     MaxX = win->Width - 4;
  864.  
  865.     SetAPen( RP, BPen );
  866.     RectFill( RP, 3, Top, MaxX, Bottom );
  867.  
  868.     SetAPen( RP, Pen1);
  869.     Move( RP, 3, Bottom);
  870.     Draw( RP, 3, Top );
  871.     Draw( RP, MaxX, Top );
  872.     SetAPen( RP, Pen2);
  873.     Draw( RP, MaxX, Bottom );
  874.     Draw( RP, 4, Bottom );
  875.  
  876.     SetAPen( RP, TextPen);
  877.  
  878.     Width = win->Width - 10;
  879.  
  880.     Len = TextFit( RP, name, (ULONG)strlen(name), &temp_te, NULL, 1,
  881.         Width, 1 + font_height);
  882.  
  883.     TextWidth = temp_te.te_Extent.MaxX - temp_te.te_Extent.MinX;
  884.  
  885.     Move( RP, 5 + (Width - TextWidth)/2 - temp_te.te_Extent.MinX,
  886.         3 + Top + tf->tf_Baseline );
  887.     Text( RP, name, Len );
  888. }
  889.  
  890. static void PUMG_DrawPopupMenuImage( struct RastPort *RP,
  891.                                      ULONG Pen1,
  892.                                      ULONG Pen2,
  893.                                      ULONG Left,
  894.                                      ULONG Top,
  895.                                      LONG Height )
  896. {
  897.     int count, i;
  898.  
  899.     if( Height > 0 ) {
  900.         SetAPen( RP, Pen1 );
  901.         Move(RP, Left, Top + Height - 1 );
  902.         Draw(RP, Left, Top );
  903.  
  904.         count = (Height-1) / 4;
  905.         for( i = 0; i <= count; i++ ) {
  906.             Move(RP, Left, Top + i*4);
  907.             Draw(RP, 10 + Left, Top + i*4);
  908.         }
  909.  
  910.         /* Draw other horizontal bar. */
  911.         Move( RP, 16 + Left, Top);
  912.         Draw( RP, 16 + Left, Top + Height);
  913.  
  914.  
  915.         SetAPen( RP, Pen2 );
  916.         Move(RP, 1 + Left, Top + Height - 1);
  917.         Draw(RP, 11 + Left, Top + Height - 1);
  918.         Draw(RP, 11 + Left, Top);
  919.  
  920.         if( Height >= 4 ) {
  921.             count = (Height-4) / 4;
  922.             for( i = 0; i <= count; i++ ) {
  923.                 Move(RP, 1 + Left, 3 + Top + i*4);
  924.                 Draw(RP, 11 + Left, 3 + Top + i*4);
  925.             }
  926.         }
  927.  
  928.         /* Draw other horizontal bar. */
  929.         Move( RP, 15 + Left, Top);
  930.         Draw( RP, 15 + Left, Top + Height );
  931.     }
  932. }
  933.  
  934.